package concurrent.c_006_Lock;

import lombok.AllArgsConstructor;
import util.TimeUtil;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * https://blog.csdn.net/f641385712/article/details/85014986
 * Semaphore 是 synchronized 的加强版，作用是控制线程的并发数量。就这一点而言，单纯的 synchronized 关键字是实现不了的。
 * * Semaphore(int permits):构造方法，创建具有给定许可数的计数信号量并设置为非公平信号量。
 * * Semaphore(int permits,boolean fair):构造方法，当fair等于true时，创建具有给定许可数的计数信号量并设置为公平信号量。
 * * void acquire():从此信号量获取一个许可前线程将一直阻塞。相当于一辆车占了一个车位。
 * * void acquire(int n):从此信号量获取给定数目许可，在提供这些许可前一直将线程阻塞。比如n=2，就相当于一辆车占了两个车位。
 * * void release():释放一个许可，将其返回给信号量。就如同车开走返回一个车位。
 * * void release(int n):释放n个许可。
 * * int availablePermits()：当前可用的许可数。
 * * boolean hasQueuedThreads():查询是否有线程正在等待获取。
 */
public class T11_Semaphore {
    // 控制三个停车位，true 表示为公平锁，默认非公平
    static final Semaphore semaphore = new Semaphore(3, true);
    // 线程池：核心线程数可以有 5 个
    static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 60,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>());

    @AllArgsConstructor
    private static class CarThread extends Thread { // 一辆车代表一个进程 进去提车系统
        private final String name; // 品牌名称

        @Override
        public void run() {
            try {
                semaphore.acquire(); // 尝试去获取许可，进入停车
                System.out.printf("%s：[%s]进入停车场年，当前时间为：%s%n",
                        Thread.currentThread().getName(), name, TimeUtil.nowDateTime());
                Thread.sleep(1000); // 模拟停车时长，停后离开
                System.out.printf("%s：[%s]要准备离开停车场，当前剩余空位[%d]，当前时间为：%s%n",
                        Thread.currentThread().getName(), name, semaphore.availablePermits(), TimeUtil.nowDateTime());
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        String[] name = {"奔驰", "宝马", "奥迪", "大众", "尼桑", "魏派WEY", "领克"};
        // 一次性来了 7 辆车
        for (String s : name) {
            threadPool.execute(new CarThread(s));
        }
        threadPool.shutdown();
    }

    public static void demo() {
        // Semaphore s = new Semaphore(2);
        Semaphore s = new Semaphore(2, true);
        // 允许一个线程同时执行
        // Semaphore s = new Semaphore(1);

        new Thread(() -> {
            try {
                // 在 semaphore.acquire() 和 semaphore.release()之间的代码，同一时刻只允许制定个数的线程进入
                // 因为 semaphore 的构造方法是 2，则同一时刻只允许两个线程进入，其他线程只能等待。
                s.acquire();
                System.out.println("T1 running...");
                Thread.sleep(200);
                System.out.println("T1 running...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                s.release();
            }
        }).start();

        new Thread(() -> {
            try {
                s.acquire();
                System.out.println("T2 running...");
                Thread.sleep(200);
                System.out.println("T2 running...");
                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
